import datetime
import math
import threading

import pandas as pd
import requests
import json
from zhdate import ZhDate
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from tools.loggingConfig import logger

seleniumChromeHost = "selenium_chrome"
# seleniumChromeHost = "192.168.33.102"


class WeChatPublicPlatform:
    """
    微信公众平台配置类
    """

    def __init__(self):
        """
        初始化配置类
        """

        # 唯一凭证
        self.appID = "wxa5cdc15ec4111409"
        self.appSecret = "071a22ddd50b076a6535a017d8f43243"

        # 全局唯一接口调用凭据
        self.accessToken = self.getAccessToken()

        # 更新accessToken的间隔时间(小时)
        self.intervalTimeForUpdateAccessToken = 2

        self.lock = threading.Lock()

    def getAccessToken(self):
        """
        更新accessToken
        @return: accessToken or None
        """

        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36'
        }

        getAccessTokenUrl = f"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" \
                            f"&appid={self.appID}" \
                            f"&secret={self.appSecret}"

        try:
            response = requests.get(url=getAccessTokenUrl, headers=headers).json()
            accessToken = response.get('access_token')

            if not accessToken:
                raise Exception

            self.accessToken = accessToken

            logger.info("accessToken更新成功")

            return self.accessToken

        except Exception as e:
            logger.error(f"accessToken更新失败: {e}")
            return None

    def sendMsg(self, userOpenID, templateMethod, **templateMethodKwargs):
        """
        发送消息
        @param userOpenID: 用户OpenID
        @param templateMethod: 对应MessageTemplate的模板方法
        @param templateMethodKwargs: 传递给MessageTemplate方法的参数
        @return: True or False
        """

        # 发送消息请求体格式
        dataFormat = {
            "touser": "",
            "template_id": "",
            "url": "",
            "topcolor": "#FF0000",
            "data": {}
        }

        sendMsgUrl = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={}"

        try:
            url = sendMsgUrl.format(self.accessToken)

            with self.lock:
                result = templateMethod(**templateMethodKwargs)

            dataFormat['touser'] = userOpenID
            dataFormat['template_id'] = result["template_id"]
            dataFormat['url'] = result["url"]
            dataFormat['data'] = result["templateData"]

            dataFormat = json.dumps(dataFormat, ensure_ascii=False).encode('utf-8')
            response = requests.post(url=url, data=dataFormat).json()

            if response.get("errcode") != 0:
                raise Exception

            logger.info(f"发送成功 {str(templateMethod)}")

            return True

        except Exception as e:
            logger.error(f"发送失败 {str(templateMethod)} {e}")
            return False


class MessageTemplate:
    """
    每个方法对应一个推送模板
    应包含渲染后的数据

    必须返回的参数:
    1. url: 用户收到通知后点击进去的链接
    2. template_id: 微信公众平台对应模板的模板ID
    3. template: 包含模板样式和数据整体
    """

    @staticmethod
    def daily(**kwargs):
        """
        日常消息模板
        @return:
        """
        url = ""
        template_id = "6jzC_dk5TfUvtDmioMCmLEP_cjtqQEKLG38Liq34Nts"
        template = {
            "date": {
                "value": "",
                "color": "#797D9DFF"
            },
            "weather": {
                "value": "",
                "color": "#FF99CC"
            },
            "temperatures": {
                "value": "",
                "color": "#FF99CC"
            },
            "loveDate": {
                "value": "",
                "color": "#FF99CC"
            },
            "birthday": {
                "value": "",
                "color": "#FF99CC"
            },
            "otherInfo": {
                "value": "",
                "color": "#FF99CC"
            },
            "footerInfo": {
                "value": "",
                "color": "#FF99CC"
            },
        }

        # 获取时间date
        date = datetime.datetime.now().strftime("%Y-%m-%d %A")

        # 城市
        city = {
            "武汉市": "101200101"
        }

        # 获取天气weather temperatures
        weatherApi = f"http://t.weather.sojson.com/api/weather/city/{city['武汉市']}"
        response = requests.get(url=weatherApi).json()
        today_info = response["data"]["forecast"][0]
        weather_type_text = today_info['type']
        weather_type = weather_type_text
        if "晴" in weather_type_text:
            weather_type = "☀"
        if "云" in weather_type_text:
            weather_type = "⛅"
        if "阴" in weather_type_text:
            weather_type = "⛅"
        if "雨" in weather_type_text:
            weather_type = "☔"
        if "雪" in weather_type_text:
            weather_type = "❄️"
        weather = f"{weather_type} | {today_info['notice']}~"
        temperatures = f"{today_info['low'].split(' ')[1]} ~ {today_info['high'].split(' ')[1]}"

        # 获取loveDate
        start_love_date = datetime.datetime(2021, 9, 21)
        today = datetime.datetime.now()
        now_love_date = datetime.datetime(today.year, today.month, today.day)
        love_date = str((now_love_date - start_love_date).days)

        # 获取birthday
        today = datetime.datetime.now()
        this_year_birthday = ZhDate(today.year, 12, 8).to_datetime()
        birthday = str(this_year_birthday.toordinal() - today.toordinal())

        # 其他内容
        other_info = ""
        if "雨" in weather_type_text:
            other_info = "!!!今天会下雨, 出门记得带伞噢~"
        if "雪" in weather_type_text:
            other_info = "!!!今天会下雪, 出门记得带伞噢~"

        # 获取footerInfo
        footer_info = "--Have a good day!--"

        # 数据填充
        template["date"]["value"] = date
        template["weather"]["value"] = weather
        template["temperatures"]["value"] = temperatures
        template["loveDate"]["value"] = love_date
        template["birthday"]["value"] = birthday
        template["otherInfo"]["value"] = other_info
        template["footerInfo"]["value"] = footer_info

        return {"template_id": template_id, "templateData": template, "url": url}

    @staticmethod
    def tomorrowTimeTable(username, password, studentName, **kwargs):
        """
        明天课表消息模板
        @param username: 教务系统 用户名
        @param password: 教务系统 密码
        @param studentName: 上课人
        @return:
        """

        url = ""
        template_id = "o_BNu54KRASTLOR4oOEYkNcOAw9gXDjHv8ZoHKq4otg"
        template = {
            "studentName": {
                "value": "",
                "color": "black"
            },
            "date": {
                "value": "",
                "color": "#797D9DFF"
            },
            "timetable": {
                "value": "",
                "color": "#FF99CC"
            },
            "footerInfo": {
                "value": "",
                "color": "#FF99CC"
            },
        }

        """
        爬取课表
        使用selenium无浏览器模式
        """
        # 配置相关参数
        options = webdriver.ChromeOptions()
        options.add_argument('window-size=1920x3000')  # 设置浏览器分辨率
        options.add_argument('--disable-gpu')  # 谷歌文档提到需要加上这个属性来规避bug
        options.add_argument('--hide-scrollbars')  # 隐藏滚动条，应对一些特殊页面
        options.add_argument('blink-settings=imagesEnabled=false')  # 不加载图片，提升运行速度
        options.add_argument('--headless')  # 浏览器不提供可视化界面。Linux下如果系统不支持可视化不加这条会启动失败
        options.add_argument('--no-sandbox')  # 解决DevToolsActivePort文件不存在的报错
        options.add_argument('-enable-webgl')  # 解决DevToolsActivePort文件不存在的报错

        driver = webdriver.Remote(f"http://{seleniumChromeHost}:4444/wd/hub", options=options)

        # 开始爬取
        login_url = "https://jxgl.wtbu.edu.cn/eams/login.action"
        driver.get(login_url)
        # 隐性等待, 最长300秒
        driver.implicitly_wait(300)

        # 寻找两输入框
        username_ele = driver.find_element(By.ID, 'username')
        password_ele = driver.find_element(By.ID, 'password')

        # 先清空两输入框
        username_ele.clear()
        password_ele.clear()

        # 键入用户名和密码
        username_ele.send_keys(username)
        password_ele.send_keys(password)

        # 寻找登录按钮
        login_ele = driver.find_element(By.NAME, 'submitBtn')

        # 点击登录按钮
        login_ele.click()

        # 等待加载完毕
        driver.implicitly_wait(300)

        # 循环所有打开页面，找到目的跳转页
        for handle in driver.window_handles:
            driver.switch_to.window(handle)
            # 以页面的url作为判断依据
            if 'home.action' in driver.current_url:
                break

        # 点击 我的课表
        my_class_ele = driver.find_element(By.XPATH, '//a[contains(text(),"我的课表")]')
        my_class_ele.click()

        # 循环所以打开页面，找到目的跳转页
        for handle in driver.window_handles:
            driver.switch_to.window(handle)
            # 以页面的url作为判断依据
            if 'courseTable.action' in driver.current_url:
                break

        # 等待渲染，直到出现id="manualArrangeCourseTable"完毕
        WebDriverWait(driver, 300).until(
            EC.presence_of_element_located((By.XPATH, '//table[@id="manualArrangeCourseTable"]'))
        )

        # 本学期开学日期
        startSchoolDate = datetime.datetime(2023, 3, 6)

        # 爬取课表的周数(当前周)
        week = math.ceil(((datetime.datetime.today() + datetime.timedelta(
            days=1) - startSchoolDate).days + startSchoolDate.isoweekday()) / 7)

        # 查看本周星期几的课表(1-7)
        weekday = (datetime.datetime.today() + datetime.timedelta(days=1)).isoweekday()

        # 跳转查询第n周课表
        week_ele = driver.find_element(By.XPATH, f'//option[contains(text(),"第{week}周")]')
        week_ele.click()

        # 等待渲染，直到出现完毕
        WebDriverWait(driver, 300).until(
            EC.presence_of_element_located(
                (By.XPATH, f'//div[@id="messageZone" and contains(@style,"visibility: hidden")]'))
        )

        # 包含课表内容的HTML文本
        week_class_table_html_text = driver.find_element(By.XPATH,
                                                         '//table[@id="manualArrangeCourseTable"]').get_attribute(
            "outerHTML")

        # 关闭浏览器，并离开
        driver.close()
        driver.quit()

        # 解析爬取到的课表html table内容
        df = pd.DataFrame()
        rh = pd.read_html(week_class_table_html_text).pop()
        data = pd.concat([df, rh])
        week_str = "星期一星期二星期三星期四星期五星期六星期日"
        week_str_index = (weekday - 1) * 3
        week_str_result = week_str[week_str_index: week_str_index + 3]
        weekday_data = data.loc[:, week_str_result]
        # 拼接课表内容
        time_table = ""
        for num in range(0, 12, 2):
            class_content = str(weekday_data.loc[num])
            class_content_next = str(weekday_data.loc[num + 1])
            if class_content == class_content_next:
                if class_content != "nan":
                    s = class_content.split("(")
                    class_content = f"{s[0]}-{s[-1][:-1].split(',')[1]}"
                    time_table += f"第{num + 1}~{num + 2}节：{class_content}\n\n"
            else:
                if class_content != "nan":
                    s = class_content.split("(")
                    class_content = f"{s[0]}-{s[-1][:-1].split(',')[1]}"
                    time_table += f"第{num + 1}小节：{class_content}\n\n"
                if class_content_next != "nan":
                    s = class_content_next.split("(")
                    class_content_next = f"{s[0]}-{s[-1][:-1].split(',')[1]}"
                    time_table += f"第{num + 2}小节：{class_content_next}\n\n"

        if not time_table.strip():
            time_table = "明天无课~"
        else:
            time_table = time_table.strip("\n\n")

        # 获取时间date
        date = f'{(datetime.datetime.today() + datetime.timedelta(days=1)).strftime("%Y-%m-%d %A")}\n第{week}周 - {week_str_result}'
        # 获取footerInfo
        footer_info = "--Have a good day!--"

        # 数据填充
        template["studentName"]["value"] = studentName
        template["date"]["value"] = date
        template["timetable"]["value"] = time_table
        template["footerInfo"]["value"] = footer_info

        return {"template_id": template_id, "templateData": template, "url": url}


# 所有用户的OpenID
allUserInfo = {
    "刘雷": {
        "userOpenId": "oxy8F5wQX6FTxf7ryMXMiDpjunEQ",
        "templateMethodKwargs": {
            'username': "22204055",
            'password': "Ll430621+-",
            "studentName": "刘雷",
        },
        "customNeedTemplateMethod": {
            "7:30": [MessageTemplate.daily],
            "20:00": [MessageTemplate.tomorrowTimeTable]
        },
        "shareUserList": {
            "吴田恬": [MessageTemplate.tomorrowTimeTable]
        }
    },

    "吴田恬": {
        "userOpenId": "oxy8F5_5hgTjMKzgVd1xSJ0o2jac",
        "templateMethodKwargs": {
            'username': "22204136",
            'password': "123456789Wtt",
            "studentName": "吴田恬",
        },
        "customNeedTemplateMethod": {
            "7:30": [MessageTemplate.daily],
            "20:00": [MessageTemplate.tomorrowTimeTable]
        },
        "shareUserList": {
            "刘雷": [MessageTemplate.tomorrowTimeTable]
        }
    },

    "彭云飞": {
        "userOpenId": "oxy8F5zmIDQ03vCzpQg2RpYWiAHs",
        "templateMethodKwargs": {
            'username': "22204084",
            'password': "Pengyunfei2001",
            "studentName": "彭云飞",
        },
        "customNeedTemplateMethod": {
            "20:00": [MessageTemplate.tomorrowTimeTable]
        },
        "shareUserList": {}
    },

}
